package com.aispeech.asr.apis.ssc.controller;

import com.aispeech.asr.apis.ssc.config.AppConfig;
import com.aispeech.asr.apis.ssc.data.form.Audio;
import com.aispeech.asr.apis.ssc.data.form.SscForm;
import com.aispeech.asr.apis.ssc.service.SscService;
import com.aispeech.asr.comm.data.vo.Result;
import com.aispeech.asr.comm.enums.ErrorCode;
import com.aispeech.asr.comm.exceptions.AsrException;
import com.aispeech.asr.webase.controller.Base;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

@RestController
@CrossOrigin
@Slf4j
public class Ssc extends Base {
    @Autowired
    ObjectMapper objectMapper;
    @Autowired
    AppConfig config;

    @Autowired
    SscService service;

    @PostMapping(value = "/ssc-api/v1/ssc", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Mono<Result> post(@RequestPart("params") Part paramsPart,
                             @RequestPart("file") FilePart filePart,
                             @RequestParam("productId") String productId) {
        return Mono
                .zip(
                        DataBufferUtils.join(paramsPart.content()),
                        DataBufferUtils.join(filePart.content())
                ).flatMap(tuple -> onReceived(productId, tuple));
    }

    private Mono<Result> onReceived(String productId, Tuple2<DataBuffer, DataBuffer> tuple) {
        DataBuffer formBuffer = tuple.getT1();
        DataBuffer audioBuffer = tuple.getT2();

        try {
            InputStream inputStream = audioBuffer.asInputStream(true);
            SscForm form = objectMapper.readValue(formBuffer.asInputStream(true), SscForm.class);

            Audio audio = form.getAudio();

            if (audio == null || audio.getSampleRate() == 0) {
                log.warn("param 'audio' error. form: {}", form);
                return Mono.just(Result.fail(ErrorCode.ERR_PARAMS_MISSED.errid, "missing param 'audio' or parsing failed"));
            }

            if (!"wav".equalsIgnoreCase(audio.getAudioType())) {
                return Mono.just(Result.fail(ErrorCode.ERR_PARAMS_MISSED.errid, "sorry, current version only supports WAV audio."));
            }
            int audioLength = audioBuffer.readableByteCount();
            if (audioLength > config.getMaxSize()) {
                log.warn("param 'audio' error. form: {}", form);
                return Mono.just(Result.fail(ErrorCode.ERR_AUDIO_CREATE_FAILED.errid, "over audio size limit. current: " + audioLength));
            } else {
                form.setLength(audioLength);
                form.setDuration(AppConfig.pcmBytesToMsecs(audioLength, audio.getSampleRate(), audio.getChannel(), audio.getSampleBytes()));
            }

            form.setProductId(productId);
            String sessionId = UUID.randomUUID().toString().replaceAll("-", "");
            log.info("[ onReceived ]: sessionId: {}, form: {}", sessionId, form);

            File fileDest = new File(String.format("/audios/%s/%s.%s",
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")),
                    sessionId,
                    audio.getAudioType()));
            if (!fileDest.getParentFile().exists()) fileDest.getParentFile().mkdir();
            Files.copy(inputStream, Paths.get(fileDest.toURI()));

            return service
                    .process(sessionId, form, fileDest);
        } catch (IOException ex) {
            String str = "";

            if (ex instanceof UnrecognizedPropertyException) {
                UnrecognizedPropertyException unrecognizedPropertyException = (UnrecognizedPropertyException) ex;
                str = unrecognizedPropertyException.getMessage();
            } else {
                str = "parsing failed";
            }

            return Mono.error(new AsrException(ErrorCode.ERR_PARAMS_MISSED.errid, str.split("\n")[0]));
        } finally {
            DataBufferUtils.release(formBuffer);
            DataBufferUtils.release(audioBuffer);
        }
    }
}
